import os
import re
import json


from kubernetes import client, config
from eval_lib.common.logger import get_logger
from common import const
log = get_logger()

class KubeController():

    def __init__(self, namespace=None) -> None:
        config.load_incluster_config()
        self.namespace = namespace if namespace else os.environ.get('POD_NAMESPACE')
        self.core_api = client.CoreV1Api()
        self.batch_api = client.BatchV1Api() 

    def get_pods_status(self, name_pattern: str) -> dict:
        result = {}
        pattern = re.compile(name_pattern)
        pod_list = self.core_api.list_namespaced_pod(self.namespace)
        for pod in pod_list.items:
            if re.fullmatch(pattern, pod.metadata.name):
                result[pod.metadata.name] = pod.status.phase
        return result
    
    def delete_pod(self, name_pattern):
        pattern = re.compile(name_pattern)
        pod_list = self.core_api.list_namespaced_pod(self.namespace)
        for pod in pod_list.items:
            if re.fullmatch(pattern, pod.metadata.name):
                try:
                    self.core_api.delete_namespaced_pod(pod.metadata.name, namespace=self.namespace)
                except client.exceptions.ApiException as e:
                    log.error(f"Error deleting Pod '{pod.metadata.name}': {e}")

    def create_configmap(self, cm_name: str, data: dict) -> None:
        labels = {
            "app": const.RUNNER_LABEL_APP, 
            "component": const.RUNNER_LABEL_COMPONENT
        }
        body = client.V1ConfigMap(
            metadata=client.V1ObjectMeta(name=cm_name, labels=labels),
            data=data
        )
        try:
            self.core_api.create_namespaced_config_map(self.namespace, body)
        except client.exceptions.ApiException as e:
            log.error(f"Error creating ConfigMap '{cm_name}': {e}")

    def delete_configmap(self, cm_name: str) -> None:
        try:
            self.core_api.delete_namespaced_config_map(name=cm_name, namespace=self.namespace)
        except client.exceptions.ApiException as e:
            log.error(f"Error deleting ConfigMap '{cm_name}': {e}")

    def create_runner_template(self, name, image, configmap_name):
        labels = {
            "app": const.RUNNER_LABEL_APP, 
            "component": const.RUNNER_LABEL_COMPONENT
        }
        config_map = client.V1ConfigMapVolumeSource(
            name=configmap_name,
            items=[{"key":"runnerConfig","path": "eval-runner.yaml"}],
        )
        config_map_volume = client.V1Volume(
            name="runner-config-v", 
            config_map=config_map
        )
        volume_mount = client.V1VolumeMount(
            name="runner-config-v", 
            mount_path=const.RUNNER_CONFIG_PATH,
            sub_path=os.path.basename(const.RUNNER_CONFIG_PATH)
        )
        container = client.V1Container(
            name=name,
            image=image,
            volume_mounts=[volume_mount],
            image_pull_policy="Always"
        )
        spec = client.V1PodSpec(
            containers=[container], 
            volumes=[config_map_volume],
            restart_policy="Never",
            dns_config={"nameservers": [const.DNS_NAME_SERVER]}
        )
        template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(labels=labels),
            spec=spec
        )
        return template

    def create_job_with_configmap(self, job_name: str, image_tag: str, configmap_name: str) -> None:
        labels = {
            "app": const.RUNNER_LABEL_APP, 
            "component": const.RUNNER_LABEL_COMPONENT
        }
        image = f"{const.RUNNER_IMAGE}:{image_tag}"
        template = self.create_runner_template(job_name, image, configmap_name)
        spec = client.V1JobSpec(
            template=template,
            backoff_limit=0
        )
        job = client.V1Job(
            api_version="batch/v1",
            kind="Job",
            metadata=client.V1ObjectMeta(name=job_name,labels=labels),
            spec=spec
        )
        try:
            self.batch_api.create_namespaced_job(self.namespace, job)
        except client.exceptions.ApiException as e:
            log.error(f"Error creating Job '{job_name}': {e}")
        
    def delete_job(self, job_name: str) -> None:
        try:
            self.batch_api.delete_namespaced_job(name=job_name, namespace=self.namespace)
        except client.exceptions.ApiException as e:
            log.error(f"Error deleting Job '{job_name}': {e}")
